/*
 * $XFree86: xc/programs/Xserver/hw/kdrive/kmode.c,v 1.6 2001/03/30 02:15:20 keithp Exp $
 *
 * Copyright 1999 SuSE, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of SuSE not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  SuSE makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Keith Packard, SuSE, Inc.
 */

#include "kdrive.h"

const KdMonitorTiming  kdMonitorTimings[] = {
    /*	H	V	Hz	KHz */
		/*  FP	    BP	    BLANK   POLARITY */
    /* Other VESA modes */
    {	640,	350,	85,	31500,			    /* VESA */
		    32,	    96,	    192,    KdSyncPositive, /* 37.861 */
		    32,	    60,	    95,	    KdSyncNegative, /* 85.080 */
    },
    {	640,	400,	85,	31500,			    /* VESA */
		    32,	    96,	    192,    KdSyncNegative, /* 37.861 */
		    1,	    41,	    45,	    KdSyncPositive, /* 85.080 */
    },
    {	720,	400,	85,	35500,			    /* VESA */
		    36,	    108,    216,    KdSyncNegative, /* 37.927 */
		    1,	    42,	    46,	    KdSyncPositive, /* 85.039 */
    },


    /* Modeline "720x576"     29.000 720  736  800  880   576  577  580  625 */
    {
        720,    576,    52,     32954,                      /* PAL Video */
        16,     80,  160, KdSyncPositive, /* 32.954 */
        1,      45,   49, KdSyncPositive, /* 52.727 */
    },
    
    /* 640x480 modes */
    {	640,	480,	85,	36000,			    /* VESA */
		    56,	    80,	    192,    KdSyncNegative, /* 43.269 */
		    1,	    25,	    29,	    KdSyncNegative, /* 85.008 */
    },
    {	640,	480,	75,	31500,			    /* VESA */
		    16,	    120,    200,    KdSyncNegative, /* 37.500 */
		    1,	    16,	    20,	    KdSyncNegative, /* 75.000 */
    },
    {	640,	480,	72,	31500,			    /* VESA */
		    16,	    120,    176,    KdSyncNegative, /* 37.861 */
		    1,	    20,	    24,	    KdSyncNegative, /* 72.809 */
    },
    {	640,	480,	60,	25175,			    /* VESA */
		   16,	    48,	    160,    KdSyncNegative, /* 31.469 */
		   10,	    33,	    45,	    KdSyncNegative, /* 59.940 */
    },
    
    /* 800x600 modes */
    {	800,	600,	85,	56250,			    /* VESA */
		    32,	    152,    248,    KdSyncPositive, /* 53.674 */
		    1,	    27,	    31,	    KdSyncPositive, /* 85.061 */
    },
    {	800,	600,	75,	49500,			    /* VESA */
		    16,	    160,    256,    KdSyncPositive, /* 46.875 */
		    1,	    21,	    25,	    KdSyncPositive, /* 75.000 */
    },
    /* DEFAULT */
#define MONITOR_TIMING_DEFAULT	9
    {	800,	600,	72,	50000,			    /* VESA */
		    56,	    64,	    240,    KdSyncPositive, /* 48.077 */
		    37,	    23,	    66,	    KdSyncPositive, /* 72.188 */
    },
    {	800,	600,	60,	40000,			    /* VESA */
		    40,	    88,	    256,    KdSyncPositive, /* 37.879 */
		    1,	    23,	    28,	    KdSyncPositive, /* 60.317 */
    },
    {	800,	600,	56,	36000,			    /* VESA */
		    24,	    128,    224,    KdSyncPositive, /* 35.156 */
		    1,	    22,	    25,	    KdSyncPositive, /* 56.250 */
    },
    
    /* 1024x768 modes */
    {	1024,	768,	85,	94500,			    /* VESA */
		    48,	    208,    352,    KdSyncPositive, /* 68.677 */
		    1,	    36,	    40,	    KdSyncPositive, /* 84.997 */
    },
    {	1024,	768,	75,	78750,			    /* VESA */
		    16,	    176,    288,    KdSyncPositive, /* 60.023 */
		    1,	    28,	    32,	    KdSyncPositive, /* 75.029 */
    },
    {	1024,	768,	70,	75000,			    /* VESA */
		    24,	    144,    304,    KdSyncNegative, /* 56.476 */
		    3,	    29,	    38,	    KdSyncNegative, /* 70.069 */
    },
    {	1024,	768,	60,	65000,			    /* VESA */
		    24,	    160,    320,    KdSyncNegative, /* 48.363 */
		    3,	    29,	    38,	    KdSyncNegative, /* 60.004 */
    },
    
    /* 1152x864 mode */
    {	1152,	864,	75,	108000,			    /* VESA */
		    64,	    256,    448,    KdSyncPositive, /* 67.500 */
		    1,	    32,	    36,	    KdSyncPositive, /* 75.000 */
    },
    
    /* 1152x900 modes */
    {	1152,	900,	85,	122500,			    /* ADDED */
		    48,	    208,    384,    KdSyncPositive, /* 79.753 */
		    1,	    32,	    38,	    KdSyncPositive, /* 85.024 */
    },
    {	1152,	900,	75,	108250,			    /* ADDED */
		    32,	    208,    384,    KdSyncPositive, /* 70.475 */
		    1,	    32,	    38,	    KdSyncPositive, /* 75.133 */
    },
    {	1152,	900,	70,	100250,			    /* ADDED */
		    32,	    208,    384,    KdSyncPositive, /* 65.267 */
		    2,	    32,	    38,	    KdSyncPositive, /* 69.581 */
    },
    {	1152,	900,	66,	95000,			    /* ADDED */
		    32,	    208,    384,    KdSyncPositive, /* 61.849 */
		    1,	    32,	    38,	    KdSyncPositive, /* 65.937 */
    },
    
    /* 1280x960 modes */
    {	1280,	960,	85,	148500,			    /* VESA */
		    64,	    224,    448,    KdSyncPositive, /* 85.938 */
		    1,	    47,	    51,	    KdSyncPositive, /* 85.002 */
    },
    {	1280,	960,	60,	108000,			    /* VESA */
		    96,	    312,    520,    KdSyncPositive, /* 60.000 */
		    1,	    36,	    40,	    KdSyncPositive, /* 60.000 */
    },

    /* 1280x1024 modes */
    {	1280,	1024,	85,	157500,			    /* VESA */
		    64,	    224,    448,    KdSyncPositive, /* 91.146 */
		    1,	    44,	    48,	    KdSyncPositive, /* 85.024 */
    },
    {	1280,	1024,	75,	135000,			    /* VESA */
		    16,	    248,    408,    KdSyncPositive, /* 79.976 */
		    1,	    38,	    42,	    KdSyncPositive, /* 75.025 */
    },
    {	1280,	1024,	60,	108000,			    /* VESA */
		    48,	    248,    408,    KdSyncPositive, /* 63.981 */
		    1,	    38,	    42,	    KdSyncPositive, /* 60.020 */
    },

    /* 1600x1200 modes */
    {	1600,	1200,	85,	229500,			    /* VESA */
		    64,	    304,    560,    KdSyncPositive, /* 106.250 */
		    1,	    46,	    50,	    KdSyncPositive, /* 85.000 */
    },
    {	1600,	1200,	75,	202500,			    /* VESA */
		    64,	    304,    560,    KdSyncPositive, /* 93.750 */
		    1,	    46,	    50,	    KdSyncPositive, /* 75.000 */
    },
    {	1600,	1200,	70,	189000,			    /* VESA */
		    64,	    304,    560,    KdSyncPositive, /* 87.500 */
		    1,	    46,	    50,	    KdSyncPositive, /* 70.000 */
    },
    {	1600,	1200,	65,	175500,			    /* VESA */
		    64,	    304,    560,    KdSyncPositive, /* 81.250 */
		    1,	    46,	    50,	    KdSyncPositive, /* 65.000 */
    },
    {	1600,	1200,	60,	162000,			    /* VESA */
		    64,	    304,    560,    KdSyncPositive, /* 75.000 */
		    1,	    46,	    50,	    KdSyncPositive, /* 60.000 */
    },

    /* 1792x1344 modes */
    {	1792,	1344,	85,	301500,			    /* ADDED */
		    96,	    352,    672,    KdSyncNegative, /* 122.362 */
		    1,	    92,	    96,	    KdSyncPositive, /* 84.974 */
    },
    {	1792,	1344,	75,	261000,			    /* VESA */
		    96,	    352,    664,    KdSyncNegative, /* 106.270 */
		    1,	    69,	    73,	    KdSyncPositive, /* 74.997 */
    },
    {	1792,	1344,	60,	204750,			    /* VESA */
		    128,    328,    656,    KdSyncNegative, /* 83.640 */
		    1,	    46,	    50,	    KdSyncPositive, /* 60.000 */
    },

#if 0
    { 1800, 1012, 75 },
    { 1906, 1072, 68 },
#endif

    /* 1856x1392 modes */
    {	1856,	1392,	85,	330500,			    /* ADDED */
		    160,    352,    736,    KdSyncNegative, /* 127.508 */
		    1,	    104,    108,    KdSyncPositive, /* 85.001 */
    },
    {	1856,	1392,	75,	288000,			    /* VESA */
		    128,    352,    704,    KdSyncNegative, /* 112.500 */
		    1,	    104,    108,    KdSyncPositive, /* 75.000 */
    },
    {	1856,	1392,	60,	218250,			    /* VESA */
		    96,	    352,    672,    KdSyncNegative, /* 86.333 */
		    1,	    43,	    47,	    KdSyncPositive, /* 59.995 */
    },

    /* 1920x1440 modes */
    {	1920,	1440,	85,	341750,			    /* ADDED */
		    160,    352,    760,    KdSyncNegative, /* 127.512 */
		    1,	    56,	    60,	    KdSyncPositive, /* 85.012 */
    },
    {	1920,	1440,	75,	297000,			    /* VESA */
		    144,    352,    720,    KdSyncNegative, /* 112.500 */
		    1,	    56,	    60,	    KdSyncPositive, /* 75.000 */
    },
    {	1920,	1440,	60,	234000,			    /* VESA */
		    128,    244,    680,    KdSyncNegative, /* 90.000 */
		    1,	    56,	    60,	    KdSyncPositive, /* 60.000 */
    },
};

#define NUM_MONITOR_TIMINGS (sizeof kdMonitorTimings/sizeof kdMonitorTimings[0])

const int kdNumMonitorTimings = NUM_MONITOR_TIMINGS;

const KdMonitorTiming *
KdFindMode (KdScreenInfo    *screen,
	    Bool	    (*supported) (KdScreenInfo *,
					  const KdMonitorTiming *))
{
    int			    i;
    const KdMonitorTiming   *t;

    for (i = 0, t = kdMonitorTimings; i < NUM_MONITOR_TIMINGS; i++, t++)
    {
	if ((*supported) (screen, t) &&
	    t->horizontal == screen->width &&
	    t->vertical == screen->height &&
	    (!screen->rate || t->rate <= screen->rate))
	{
	    return t;
	}
    }
    return &kdMonitorTimings[MONITOR_TIMING_DEFAULT];
}

static const KdMonitorTiming *
kdFindPrevSize (const KdMonitorTiming *old)
{
    const KdMonitorTiming   *new, *prev;

    if (old == kdMonitorTimings)
	return 0;
    new = old;
    /*
     * Search for the previous size
     */
    while (new != kdMonitorTimings)
    {
	new--;
	if (new->horizontal != old->horizontal &&
	    new->vertical != old->vertical)
	{
	    break;
	}
    }
    /*
     * Match the refresh rate (<=)
     */
    while (new != kdMonitorTimings)
    {
	prev = new - 1;
	if (prev->horizontal == new->horizontal &&
	    prev->vertical == new->vertical &&
	    prev->rate > old->rate)
	{
	    break;
	}
	new--;
    }
    return new;
}

Bool
KdTuneMode (KdScreenInfo    *screen,
	    Bool	    (*usable) (KdScreenInfo *),
	    Bool	    (*supported) (KdScreenInfo *,
					  const KdMonitorTiming *))
{
    const KdMonitorTiming   *t, *new;
    
    while (!(*usable) (screen))
    {
	/*
	 * Fix requested depth and geometry until it works
	 */
	if (screen->fb[1].depth)
	    screen->fb[1].depth = 0;
	else if (screen->fb[0].depth > 16)
	    screen->fb[0].depth = 16;
	else if (screen->fb[0].depth > 8)
	    screen->fb[0].depth = 8;
	else 
	{
	    t = kdFindPrevSize (KdFindMode (screen, supported));
	    if (!t)
		return FALSE;
	    screen->width = t->horizontal;
	    screen->height = t->vertical;
	    screen->rate = t->rate;
	}
    }
    return TRUE;
}
